﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using SFP;
using SFP.Animation2D;
using SFP.Audio;
using SFP.CollisionDetection;
using HamsterDelivery.LevelStructure.Reactions;
using HamsterDelivery.LevelStructure.GameObjects;

namespace HamsterDelivery.LevelStructure
{
    public class Level
    {
        /* Constants */
        public const double GRAVITY = 1000;
        public const int MAIN_LAYER = 2;
        public const int PLAYER_LAYER = 3;
        public const int GRAS_LAYER = 4;

        /* Game */
        private HamsterGame game;
        public int LevelCounter { get; set; }

        /* Layer Manager */
        public LayerManager LayerManager { get; private set; }
        private LayerManager.Bitmaps levelBitmaps;

        /* Game Object Types */
        public HashSet<GameObject> StreetBlocks { get; set; }
        public HashSet<GameObject> Packages { get; set; }
        public HashSet<GameObject> Letters { get; set; }
        public HashSet<GameObject> GrasBlocks { get; set; }
        
        /* Level end */
        public HashSet<GameObject> EndBlockSet { get; set; }
        public GameObject startBlock { get; set; }

        /* Global Reactions */
        public ObstacleReaction ObstaclekReaction { get; set; }
        
        /* Reactions */
        public PackageReaction PackageReaction { get; set; }
        public LetterReaction LetterReaction { get; set; }
        public GrasReaction GrasReaction { get; set; }
        public LevelEndReaction LevelEndReaction { get; set; }

        public Level(HamsterGame game)
        {
            this.game = game;

            LayerManager = new LayerManager();
            LevelCounter = 1;

            PackageReaction = new PackageReaction(game);
            ObstaclekReaction = new ObstacleReaction(game);
            GrasReaction = new GrasReaction(game);
            LetterReaction = new LetterReaction(game);
            LevelEndReaction = new LevelEndReaction(game);
        }

        public void update(double time)
        {
            foreach (LetterObject letter in Letters)
            {
                letter.update(time);
            }
        }

        public void draw(Graphics g)
        {
            LayerManager.draw(g);
        }

        public void loadLevel(int counter)
        {
            AudioPlayer.stopAll();
            AudioPlayer.loop(Resources.A_MAIN_THEME);

            // Reset layer manager and load new level
            LayerManager.clearManager();
            LayerManager.scrollToOrigin();
            LayerManager.load(ContentLoader.LoadXML("level\\level" + counter + ".xml"), game.resolution, out levelBitmaps, true);
            LayerManager.getLayer(PLAYER_LAYER).addLayerObject(game.Player);
            game.PackageScore = 0;
            game.LetterScore = 0;

            // Reference loaded level resources
            Resources.B_PACKAGE = levelBitmaps.GetBitmapByName("Package");
            Resources.B_LETTER = levelBitmaps.GetBitmapByName("Letter");

            // Organize game object types
            StreetBlocks = GameObject.GetHashSetByType(LayerManager.getLayer(MAIN_LAYER), "Street");
            Packages = GameObject.GetHashSetByType(LayerManager.getLayer(MAIN_LAYER), "Package");
            Letters = GameObject.GetHashSetByType(LayerManager.getLayer(MAIN_LAYER), "Letter");
            GrasBlocks = GameObject.GetHashSetByType(LayerManager.getLayer(GRAS_LAYER), "Gras");

            foreach (GameObject letter in new HashSet<GameObject>(Letters))
            {
                replaceGameObject(letter, new LetterObject(game), MAIN_LAYER, Letters);
            }
            Random randomSeed = new Random();
            foreach(GameObject grasBlock in new HashSet<GameObject>(GrasBlocks))
            {
                replaceGameObject(grasBlock, new GrasObject(randomSeed.Next()), GRAS_LAYER, GrasBlocks);
            }

            int x = 40;
            int y = 400;

            HashSet<GameObject> startBlockSet = GameObject.GetHashSetByType(LayerManager.getLayer(MAIN_LAYER), "PlayersStart");
            if (startBlockSet != null && startBlockSet.Count >= 1)
            {
                startBlock = startBlockSet.First<GameObject>();
                x = (int) startBlock.X;
                y = (int) startBlock.Y;
                LayerManager.getLayer(MAIN_LAYER).removeLayerObject(startBlock);
            }

            EndBlockSet = GameObject.GetHashSetByType(LayerManager.getLayer(MAIN_LAYER), "LevelEnd");

            game.Player.reset(x, y);
        }

        public void processLevelCollisions(Rectangle playerRect, Direction vMoveDirection, Direction hMoveDirection)
        {
            Collision.clean();

            // Detect collisions...
            long streetBlocksSetID = Collision.collisionsForGameObjects(StreetBlocks, playerRect, true);
            long packageSetID = Collision.collisionsForGameObjects(Packages, playerRect, false);
            long letterSetID = Collision.collisionsForGameObjects(Letters, playerRect, false);
            long grasSetID = Collision.collisionsForGameObjects(GrasBlocks, playerRect, false);
            long endBlockSetID = Collision.collisionsForGameObjects(EndBlockSet, playerRect, false);

            // Filter collisions...
            Collision.processFilters(vMoveDirection, hMoveDirection);

            // React to collisions...
            ObstaclekReaction.reactToCollision(new long[] { streetBlocksSetID });
            PackageReaction.reactToCollision(packageSetID);
            LetterReaction.reactToCollision(letterSetID);
            GrasReaction.reactToCollision(grasSetID);
            LevelEndReaction.reactToCollision(endBlockSetID);
        }

        public void removeGameObject(GameObject gameObject, int layerIndex, HashSet<GameObject> typeSet)
        {
            LayerManager.getLayer(layerIndex).removeLayerObject(gameObject);
            if (typeSet != null) typeSet.Remove(gameObject);
        }

        public void addGameObject(GameObject gameObject, int layerIndex, HashSet<GameObject> typeSet)
        {
            LayerManager.getLayer(layerIndex).addLayerObject(gameObject);
            if (typeSet != null) typeSet.Add(gameObject);
        }

        public void replaceGameObject(GameObject oldGameObj, GameObject newGameObj, int layerIndex, HashSet<GameObject> typeSet)
        {
            newGameObj.X = oldGameObj.X;
            newGameObj.Y = oldGameObj.Y;
            newGameObj.GroupColumns = oldGameObj.GroupColumns;
            newGameObj.GroupRows = oldGameObj.GroupRows;
            newGameObj.Image = oldGameObj.Image;
            newGameObj.ImagePath = oldGameObj.ImagePath;
            newGameObj.Name = oldGameObj.Name;
            newGameObj.Type = oldGameObj.Type;

            removeGameObject(oldGameObj, layerIndex, typeSet);
            addGameObject(newGameObj, layerIndex, typeSet);
        }
    }
}
